/*
 * Copyright (c) 2011, Michael van der Westhuizen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef PYXMLSEC1_UTIL_C_MCVDW_20101121
#define PYXMLSEC1_UTIL_C_MCVDW_20101121

#include "pyxmlsec.h"

#include <libxml/xpathInternals.h> /* blech */

#include <string.h>


static const char xmlDocPtrName[] = "xmlDocPtr";
static const char xmlNodePtrName[] = "xmlNodePtr";
static const char xmlNsPtrName[] = "xmlNsPtr";
static const char xmlNodeSetPtrName[] = "xmlNodeSetPtr";
static const char xmlOutputBufferPtrName[] = "xmlOutputBufferPtr";
static const char xmlParserInputBufferPtrName[] = "xmlParserInputBufferPtr";


static void * xmlsec1mod_unwrap_libxml_pointer(PyObject * obj, const char * name);


/* free the memory returned in *out using PyMem_Del */
int xmlsec1_ByteArrayOrBytesToXmlSecBytes(PyObject * in, xmlSecByte ** out, xmlSecSize * outSize)
{
    Py_ssize_t in_len;
    Py_ssize_t i;
    char * in_ptr;
    int clear_in = 0;

    if (NULL == in)
    {
        PyErr_BadArgument();
        return -1;
    }

    if (NULL == out)
    {
        PyErr_BadArgument();
        return -1;
    }

    if (NULL == outSize)
    {
        PyErr_BadArgument();
        return -1;
    }

    if (PyByteArray_Check(in))
    {
        in_len = PyByteArray_Size(in);
        in_ptr = PyByteArray_AsString(in);
    }
    else if (PyBytes_Check(in))
    {
        in_len = PyBytes_Size(in);
        in_ptr = PyBytes_AsString(in);
    }
    else if (PyUnicode_Check(in))
    {
        in = PyUnicode_AsUTF8String(in);
        clear_in = 1;
        in_len = PyBytes_Size(in);
        in_ptr = PyBytes_AsString(in);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "input must be one of the following types: bytearray, bytes or unicode");
        return -1;
    }

    if (NULL == in_ptr)
    {
        if (clear_in)
        {
            Py_CLEAR(in);
        }

        /* error already set in the case of a converison error */
        return -1;
    }

    if (NULL == (*out = PyMem_New(xmlSecByte, in_len)))
    {
        if (clear_in)
        {
            Py_CLEAR(in);
        }

        PyErr_NoMemory();
        return -1;
    }

    memset((*out), '\0', in_len);
    *outSize = (xmlSecSize)in_len;

    for (i = 0; i < in_len; ++i)
    {
        (*out)[i] = (xmlSecByte)in_ptr[i];
    }

    if (clear_in)
    {
        Py_CLEAR(in);
    }

    return 0;
}



/* free the memory returned in *out using xmlFree */
int xmlsec1_UnicodeOrBytesToXmlCharPtr(PyObject * in, xmlChar ** out, int * outSize)
{
    int in_len;
    PyObject * tmp;

    if (NULL == in)
    {
        PyErr_BadArgument();
        return -1;
    }

    if (NULL == out)
    {
        PyErr_BadArgument();
        return -1;
    }

    if (PyBytes_Check(in))
    {
        in_len = (int)PyBytes_Size(in);

        if (NULL == (*out = xmlCharStrndup(PyBytes_AsString(in), in_len)))
        {
            PyErr_NoMemory();
            return -1;
        }
    }
    else if (PyUnicode_Check(in))
    {
        if (NULL == (tmp = PyUnicode_AsUTF8String(in)))
        {
            ENSURE_EXCEPTION_SET(PyUnicode_AsUTF8String);
            return -1;
        }

        in_len = (int)PyBytes_Size(tmp);

        if (NULL == (*out = xmlCharStrndup(PyBytes_AsString(tmp), in_len)))
        {
            Py_CLEAR(tmp);
            PyErr_NoMemory();
            return -1;
        }

        Py_CLEAR(tmp);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "input must be of type bytes (string) or unicode");
        return -1;
    }

    if (NULL != outSize)
    {
        *outSize = in_len;
    }

    return 0;
}



xmlNodeSetPtr xmlsec1mod_SequenceToXmlNodeSetPtr(PyObject * obj)
{
    xmlNodeSetPtr nodeSet;
    Py_ssize_t seq_len;
    PyObject * iter;
    PyObject * item;

    if (NULL == obj)
    {
        PyErr_SetString(PyExc_ValueError, "arg may not be NULL");
        return NULL;
    }

    if (!PySequence_Check(obj))
    {
        PyErr_SetString(PyExc_TypeError, "arg must be a sequence type");
        return NULL;
    }

    if (NULL == (nodeSet = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet))))
    {
        PyErr_SetString(PyExc_MemoryError, "unable to allocate memory for an XML Node Set");
    }

    nodeSet->nodeNr = 0;

    if (-1 == (seq_len = PySequence_Length(obj)))
    {
        xmlFree(nodeSet);
        return NULL;
    }

    nodeSet->nodeMax = (int)seq_len;

    if (NULL == (nodeSet->nodeTab = (xmlNodePtr *)xmlMalloc((nodeSet->nodeMax ? nodeSet->nodeMax : 1) * sizeof(xmlNodePtr))))
    {
        xmlFree(nodeSet);
        PyErr_SetString(PyExc_MemoryError, "unable to allocate memory for XML Node Set storage");
        return NULL;
    }

    memset(nodeSet->nodeTab, '\0', (nodeSet->nodeMax ? nodeSet->nodeMax : 1) * sizeof(xmlNodePtr));

    if (NULL == (iter = PyObject_GetIter(obj)))
    {
        xmlFree(nodeSet->nodeTab);
        xmlFree(nodeSet);
        return NULL;
    }

    while ((item = PyIter_Next(iter)))
    {
        xmlNodePtr ptr;

        if (NULL == (ptr = xmlsec1mod_unwrap_xmlNodePtr(item)))
        {
            Py_DECREF(item);
            Py_DECREF(iter);
            PyErr_SetString(PyExc_ValueError, "sequence items must be of type xmlNodePtr");
            xmlFree(nodeSet->nodeTab);
            xmlFree(nodeSet);
            return NULL;
        }

        nodeSet->nodeTab[nodeSet->nodeNr++] = ptr;
        Py_DECREF(item);
    }

    Py_DECREF(iter);

    if (PyErr_Occurred())
    {
        xmlFree(nodeSet->nodeTab);
        xmlFree(nodeSet);
        return NULL;
    }

    return nodeSet;
}



#ifdef Py_CAPSULE_H
static void xmlsec1mod_xmlXPathDestructNsNode(PyObject * obj)
{
    void * obj_ = PyCapsule_GetPointer(obj);

    if (obj_)
    {
        xmlXPathNodeSetFreeNs((xmlNsPtr) obj_);
    }
}
#else
static void xmlsec1mod_xmlXPathDestructNsNode(void * obj, void * desc)
{
    if (obj)
    {
        xmlXPathNodeSetFreeNs((xmlNsPtr) obj);
    }
}
#endif


PyObject * xmlsec1mod_xmlNodeSetPtrToList(xmlNodeSetPtr ptr)
{
    PyObject * lst;
    int i;

    if (NULL == ptr)
    {
        PyErr_SetString(PyExc_ValueError, "ptr may not be NULL");
        return NULL;
    }

    if (NULL == (lst = PyList_New((Py_ssize_t)ptr->nodeNr)))
    {
        return NULL;
    }

    if (ptr->nodeNr < 1)
    {
        return lst;
    }

    for (i = 0; i < ptr->nodeNr; ++i)
    {
        PyObject * tmp;

        if (XML_NAMESPACE_DECL == ptr->nodeTab[i]->type)
        {
            PyObject * ns;
#ifdef Py_CAPSULE_H
            if (NULL == (ns = PyCapsule_New(ptr->nodeTab[i], "xmlNsPtrName", xmlsec1mod_xmlXPathDestructNsNode)))
            {
                ENSURE_EXCEPTION_SET(PyCapsule_New);
#else
            if (NULL == (ns = PyCObject_FromVoidPtrAndDesc((void *)ptr->nodeTab[i], (char *)xmlNsPtrName, xmlsec1mod_xmlXPathDestructNsNode)))
            {
                ENSURE_EXCEPTION_SET(PyCObject_FromVoidPtrAndDesc);
#endif
                Py_DECREF(lst);
                return NULL;
            }

            if (0 != PyList_Append(lst, ns))
            {
                ENSURE_EXCEPTION_SET(PyList_Append);
                Py_DECREF(ns);
                Py_DECREF(lst);
                return NULL;
            }

            ptr->nodeTab[i] = NULL; /* ensure that it doesn't get destroyed elsewhere */
            Py_DECREF(ns);
            continue;
        }

        if (NULL == (tmp = xmlsec1mod_wrap_xmlNodePtr(ptr->nodeTab[i])))
        {
            ENSURE_EXCEPTION_SET(xmlsec1mod_wrap_xmlNodePtr);
            Py_DECREF(lst);
            return NULL;
        }

        if (0 != PyList_Append(lst, tmp))
        {
            ENSURE_EXCEPTION_SET(PyList_Append);
            Py_DECREF(lst);
            Py_DECREF(tmp);
            return NULL;
        }

        Py_DECREF(tmp);
    }

    return lst;
}



/**
 * Wrap a C object into a Python object, with a name and a destructor function.
 *
 * @param ptr The C object to wrap.
 * @param name The name of the C object.
 * @param dtor The destructor function to call when this object is collected.
 * @return A python object wrapping the \c ptr object, or None if the \c ptr object is NULL.
 */
static PyObject * xmlsec1_wrapPtrInPyObject(void * ptr, const char * name)
{
    PyObject * obj;

    if (NULL == ptr)
    {
        PyErr_SetString(PyExc_ValueError, "pointer to wrap may not be NULL");
        return NULL;
    }

#ifdef Py_CAPSULE_H
    if (NULL == (obj = PyCapsule_New(ptr, name, NULL)))
    {
        ENSURE_EXCEPTION_SET(PyCapsule_New);
        return NULL;
    }
#else
    if (NULL == (obj = PyCObject_FromVoidPtrAndDesc(ptr, (void *)name, NULL)))
    {
        ENSURE_EXCEPTION_SET(PyCapsule_New);
        return NULL;
    }
#endif
    return obj;
}



static void * xmlsec1_unwrapPtrInPyObject(PyObject * obj, const char * name)
{
    void * ret;

    if (Py_None == obj)
    {
        return NULL;
    }

#ifdef Py_CAPSULE_H
    if (!PyCapsule_CheckExact(obj))
    {
        PyErr_SetString(PyExc_ValueError, "capsule does not contain an object of the correct type");
        return NULL;
    }

    if (NULL == (ret = PyCapsule_GetPointer(obj, name)))
    {
        ENSURE_EXCEPTION_SET(PyCapsule_GetPointer);
        return NULL;
    }
#else
    if (0 != strcmp(name, (const char *)PyCObject_GetDesc(obj)))
    {
        PyErr_SetString(PyExc_ValueError, "capsule does not contain an object of the correct type");
        return NULL;
    }

    if (NULL == (ret = PyCObject_AsVoidPtr(obj)))
    {
        if (!PyErr_Occurred())
        {
            PyErr_SetString(PyExc_ValueError, "capsule contains a NULL pointer");
        }

        return NULL;
    }
#endif

    return ret;
}




static const char xmlSecBase64CtxPtrName[] = XMLSEC1_XMLSECBASE64CTXPTRNAME;
static const char xmlSecBnPtrName[] = XMLSEC1_XMLSECBNPTRNAME;
static const char xmlSecBufferPtrName[] = XMLSEC1_XMLSECBUFFERPTRNAME;
static const char xmlSecKeysMngrPtrName[] = XMLSEC1_XMLSECKEYSMNGRPTRNAME;
static const char xmlSecKeyStorePtrName[] = XMLSEC1_XMLSECKEYSTOREPTRNAME;
static const char xmlSecKeyStoreIdName[] = XMLSEC1_XMLSECKEYSTOREIDNAME;
static const char xmlSecPtrListPtrName[] = XMLSEC1_XMLSECPTRLISTPTRNAME;
static const char xmlSecPtrListIdName[] = XMLSEC1_XMLSECPTRLISTIDNAME;
static const char xmlSecTransformIdName[] = XMLSEC1_XMLSECTRANSFORMIDNAME;
static const char xmlSecTransformPtrName[] = XMLSEC1_XMLSECTRANSFORMPTRNAME;
static const char xmlSecTransformCtxPtrName[] = XMLSEC1_XMLSECTRANSFORMCTXPTRNAME;
static const char xmlSecNodeSetPtrName[] = XMLSEC1_XMLSECNODESETPTRNAME;
static const char xmlSecKeyPtrName[] = XMLSEC1_XMLSECKEYPTRNAME;
static const char xmlSecKeyReqPtrName[] = XMLSEC1_XMLSECKEYREQPTRNAME;
static const char xmlSecKeyInfoCtxPtrName[] = XMLSEC1_XMLSECKEYINFOCTXPTRNAME;
static const char xmlSecKeyDataStorePtrName[] = XMLSEC1_XMLSECKEYDATASTOREPTRNAME;
static const char xmlSecKeyDataStoreIdName[] = XMLSEC1_XMLSECKEYDATASTOREIDNAME;
static const char xmlSecKeyDataIdName[] = XMLSEC1_XMLSECKEYDATAIDNAME;
static const char xmlSecKeyDataPtrName[] = XMLSEC1_XMLSECKEYDATAPTRNAME;
static const char xmlSecKeyUseWithPtrName[] = XMLSEC1_XMLSECKEYUSEWITHPTRNAME;
static const char xmlSecDSigCtxPtrName[] = XMLSEC1_XMLSECDSIGCTXPTRNAME;
static const char xmlSecCryptoDLFunctionsPtrName[] = XMLSEC1_XMLSECCRYPTODLFUNCTIONSPTRNAME;



PyObject * xmlsec1mod_wrap_xmlSecBase64CtxPtr(xmlSecBase64CtxPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecBase64CtxPtrName);
}


xmlSecBase64CtxPtr xmlsec1mod_unwrap_xmlSecBase64CtxPtr(PyObject * obj)
{
    return (xmlSecBase64CtxPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecBase64CtxPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecBnPtr(xmlSecBnPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecBnPtrName);
}


xmlSecBnPtr xmlsec1mod_unwrap_xmlSecBnPtr(PyObject * obj)
{
    return (xmlSecBnPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecBnPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecBufferPtr(xmlSecBufferPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecBufferPtrName);
}


xmlSecBufferPtr xmlsec1mod_unwrap_xmlSecBufferPtr(PyObject * obj)
{
    return (xmlSecBufferPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecBufferPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeysMngrPtr(xmlSecKeysMngrPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeysMngrPtrName);
}


xmlSecKeysMngrPtr xmlsec1mod_unwrap_xmlSecKeysMngrPtr(PyObject * obj)
{
    return (xmlSecKeysMngrPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeysMngrPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyStorePtr(xmlSecKeyStorePtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyStorePtrName);
}


xmlSecKeyStorePtr xmlsec1mod_unwrap_xmlSecKeyStorePtr(PyObject * obj)
{
    return (xmlSecKeyStorePtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyStorePtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyStoreId(xmlSecKeyStoreId ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyStoreIdName);
}


xmlSecKeyStoreId xmlsec1mod_unwrap_xmlSecKeyStoreId(PyObject * obj)
{
    return (xmlSecKeyStoreId)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyStoreIdName);
}


PyObject * xmlsec1mod_wrap_xmlSecPtrListPtr(xmlSecPtrListPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecPtrListPtrName);
}


xmlSecPtrListPtr xmlsec1mod_unwrap_xmlSecPtrListPtr(PyObject * obj)
{
    return (xmlSecPtrListPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecPtrListPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecPtrListId(xmlSecPtrListId ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecPtrListIdName);
}


xmlSecPtrListId xmlsec1mod_unwrap_xmlSecPtrListId(PyObject * obj)
{
    return (xmlSecPtrListId)xmlsec1_unwrapPtrInPyObject(obj, xmlSecPtrListIdName);
}


PyObject * xmlsec1mod_wrap_xmlSecTransformId(xmlSecTransformId ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecTransformIdName);
}


xmlSecTransformId xmlsec1mod_unwrap_xmlSecTransformId(PyObject * obj)
{
    return (xmlSecTransformId)xmlsec1_unwrapPtrInPyObject(obj, xmlSecTransformIdName);
}


PyObject * xmlsec1mod_wrap_xmlSecTransformPtr(xmlSecTransformPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecTransformPtrName);
}


xmlSecTransformPtr xmlsec1mod_unwrap_xmlSecTransformPtr(PyObject * obj)
{
    return (xmlSecTransformPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecTransformPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecTransformCtxPtr(xmlSecTransformCtxPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecTransformCtxPtrName);
}


xmlSecTransformCtxPtr xmlsec1mod_unwrap_xmlSecTransformCtxPtr(PyObject * obj)
{
    return (xmlSecTransformCtxPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecTransformCtxPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecNodeSetPtr(xmlSecNodeSetPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecNodeSetPtrName);
}


xmlSecNodeSetPtr xmlsec1mod_unwrap_xmlSecNodeSetPtr(PyObject * obj)
{
    return (xmlSecNodeSetPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecNodeSetPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyPtr(xmlSecKeyPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyPtrName);
}


xmlSecKeyPtr xmlsec1mod_unwrap_xmlSecKeyPtr(PyObject * obj)
{
    return (xmlSecKeyPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyReqPtr(xmlSecKeyReqPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyReqPtrName);
}


xmlSecKeyReqPtr xmlsec1mod_unwrap_xmlSecKeyReqPtr(PyObject * obj)
{
    return (xmlSecKeyReqPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyReqPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyInfoCtxPtr(xmlSecKeyInfoCtxPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyInfoCtxPtrName);
}


xmlSecKeyInfoCtxPtr xmlsec1mod_unwrap_xmlSecKeyInfoCtxPtr(PyObject * obj)
{
    return (xmlSecKeyInfoCtxPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyInfoCtxPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyDataStorePtr(xmlSecKeyDataStorePtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyDataStorePtrName);
}


xmlSecKeyDataStorePtr xmlsec1mod_unwrap_xmlSecKeyDataStorePtr(PyObject * obj)
{
    return (xmlSecKeyDataStorePtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyDataStorePtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyDataStoreId(xmlSecKeyDataStoreId ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyDataStoreIdName);
}


xmlSecKeyDataStoreId xmlsec1mod_unwrap_xmlSecKeyDataStoreId(PyObject * obj)
{
    return (xmlSecKeyDataStoreId)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyDataStoreIdName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyDataId(xmlSecKeyDataId ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyDataIdName);
}


xmlSecKeyDataId xmlsec1mod_unwrap_xmlSecKeyDataId(PyObject * obj)
{
    return (xmlSecKeyDataId)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyDataIdName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyDataPtr(xmlSecKeyDataPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyDataPtrName);
}


xmlSecKeyDataPtr xmlsec1mod_unwrap_xmlSecKeyDataPtr(PyObject * obj)
{
    return (xmlSecKeyDataPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyDataPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecKeyUseWithPtr(xmlSecKeyUseWithPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecKeyUseWithPtrName);
}


xmlSecKeyUseWithPtr xmlsec1mod_unwrap_xmlSecKeyUseWithPtr(PyObject * obj)
{
    return (xmlSecKeyUseWithPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecKeyUseWithPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecDSigCtxPtr(xmlSecDSigCtxPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecDSigCtxPtrName);
}


xmlSecDSigCtxPtr xmlsec1mod_unwrap_xmlSecDSigCtxPtr(PyObject * obj)
{
    return (xmlSecDSigCtxPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecDSigCtxPtrName);
}


PyObject * xmlsec1mod_wrap_xmlSecCryptoDLFunctionsPtr(xmlSecCryptoDLFunctionsPtr ptr)
{
    return xmlsec1_wrapPtrInPyObject((void *)ptr, xmlSecCryptoDLFunctionsPtrName);
}


xmlSecCryptoDLFunctionsPtr xmlsec1mod_unwrap_xmlSecCryptoDLFunctionsPtr(PyObject * obj)
{
    return (xmlSecCryptoDLFunctionsPtr)xmlsec1_unwrapPtrInPyObject(obj, xmlSecCryptoDLFunctionsPtrName);
}






/**
 * Extract a void pointer from a LibXML Python object.
 *
 * @param obj The Python object to extract the pointer from (PyCObject or PyCapsule).
 * @param name The type name stored in the Python void pointer abstraction.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 */
static void * xmlsec1mod_unwrap_libxml_pointer(PyObject * obj, const char * name)
{
    PyObject * o;
    void * ptr;

    o = NULL;
    ptr = NULL;

    if (NULL == (o = PyObject_GetAttrString(obj, "_o")))
    {
        PyErr_SetString(PyExc_TypeError, "the provided object is not a libxml Python object (no _o member)");
        return NULL;
    }

    if(PyCObject_Check(o))
    {
        if (0 != strcmp(name, (const char *)PyCObject_GetDesc(o)))
        {
            PyErr_SetString(PyExc_TypeError, "the encapsulated LibXML object is not of the correct type");
            return NULL;
        }

        if (NULL == (ptr = PyCObject_AsVoidPtr(o)))
        {
            PyErr_SetString(PyExc_TypeError, "the encapsulated LibXML object is NULL");
            return NULL;
        }
    }
#ifdef Py_CAPSULE_H
    else if (PyCapsule_CheckExact(o, PyCapsule))
    {
        if (NULL == (ptr = PyCapsule_GetPointer(o, name)))
        {
            PyErr_SetString(PyExc_TypeError, "the encapsulated LibXML object is not of the correct type");
            return NULL;
        }
    }
#endif

    if (NULL == ptr)
    {
        PyErr_SetString(PyExc_TypeError, "the encapsulation for the LibXML object is neither a PyCObject or a PyCapsule");
    }

    return ptr;
}



/**
 * Wrap a libxml2 C object into a Python object, with a name.
 *
 * @param ptr The libxml2 C object to wrap.
 * @param name The name of the C object.
 * @return A python object wrapping the \c ptr object, or None if the \c ptr object is NULL.
 */
static PyObject * xmlsec1_wrapLibxml2PtrInPyObject(void * ptr, const char * name)
{
    PyObject * mod;
    PyObject * klass;
    PyObject * dict;
    PyObject * obj;
    PyObject * res;

    if (NULL == ptr)
    {
        PyErr_SetString(PyExc_ValueError, "pointer to wrap may not be NULL");
        return NULL;
    }

    if (NULL == name)
    {
        PyErr_SetString(PyExc_ValueError, "pointer name may not be NULL");
        return NULL;
    }

    if (strncmp(name, xmlNodePtrName, strlen(xmlNodePtrName)) != 0 &&
        strncmp(name, xmlDocPtrName, strlen(xmlDocPtrName)) != 0 &&
        strncmp(name, xmlOutputBufferPtrName, strlen(xmlOutputBufferPtrName)) != 0)
    {
        PyErr_SetString(PyExc_ValueError, "pointer name not recognised");
        return NULL;
    }

    if (NULL == (mod = PyImport_ImportModuleNoBlock("libxml2")))
    {
        return NULL;
    }

    if (0 == strncmp(name, xmlDocPtrName, strlen(xmlDocPtrName)))
    {
        if (NULL == (klass = PyObject_GetAttrString(mod, "xmlDoc")))
        {
            Py_XDECREF(mod);
            return NULL;
        }
    }
    else if (0 == strncmp(name, xmlNodePtrName, strlen(xmlNodePtrName)))
    {
        if (NULL == (klass = PyObject_GetAttrString(mod, "xmlNode")))
        {
            Py_XDECREF(mod);
            return NULL;
        }
    }
    else if (0 == strncmp(name, xmlOutputBufferPtrName, strlen(xmlOutputBufferPtrName)))
    {
        if (NULL == (klass = PyObject_GetAttrString(mod, "outputBuffer")))
        {
            Py_XDECREF(mod);
            return NULL;
        }
    }
    else
    {
        PyErr_SetString(PyExc_ValueError, "pointer name not recognised");
        Py_XDECREF(mod);
        return NULL;
    }

    if (!PyCallable_Check(klass))
    {
        Py_XDECREF(klass);
        Py_XDECREF(mod);
        PyErr_SetString(PyExc_ValueError, "type is not callable");
        return NULL;
    }

#if 0
    if (NULL == (obj = PyCapsule_New(ptr, name, dtor)))
    {
        ENSURE_EXCEPTION_SET(PyCapsule_New);
        Py_XDECREF(klass);
        Py_XDECREF(mod);
        return NULL;
    }
#endif
    if (NULL == (obj = PyCObject_FromVoidPtrAndDesc(ptr, (void *)name, NULL)))
    {
        ENSURE_EXCEPTION_SET(PyCObject_FromVoidPtrAndDesc);
        Py_XDECREF(klass);
        Py_XDECREF(mod);
        return NULL;
    }

    if (NULL == (dict = Py_BuildValue("{s:O}", "_obj", obj)))
    {
        Py_XDECREF(obj);
        Py_XDECREF(klass);
        Py_XDECREF(mod);
        return NULL;
    }

    Py_XDECREF(obj);

    if (NULL == (res = PyObject_Call(klass, NULL, dict)))
    {
        Py_XDECREF(dict);
        Py_XDECREF(klass);
        Py_XDECREF(mod);
        return NULL;
    }

    Py_XDECREF(dict);
    Py_XDECREF(klass);
    Py_XDECREF(mod);
    return res;
}




PyObject * xmlsec1mod_wrap_xmlDocPtr(xmlDocPtr ptr)
{
    return xmlsec1_wrapLibxml2PtrInPyObject(ptr, xmlDocPtrName);
}


/**
 * Extract a LibXML document pointer from a Python object.
 *
 * @param obj The Python object to extract the pointer from.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 * @see xmlsec1mod_unwrap_libxml_pointer
 */
xmlDocPtr xmlsec1mod_unwrap_xmlDocPtr(PyObject * obj)
{
    return (xmlDocPtr)xmlsec1mod_unwrap_libxml_pointer(obj, xmlDocPtrName);
}



PyObject * xmlsec1mod_wrap_xmlNodePtr(xmlNodePtr ptr)
{
    return xmlsec1_wrapLibxml2PtrInPyObject(ptr, xmlNodePtrName);
}



/**
 * Extract a LibXML node pointer from a Python object.
 *
 * @param obj The Python object to extract the pointer from.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 * @see xmlsec1mod_unwrap_libxml_pointer
 */
xmlNodePtr xmlsec1mod_unwrap_xmlNodePtr(PyObject * obj)
{
    return (xmlNodePtr)xmlsec1mod_unwrap_libxml_pointer(obj, xmlNodePtrName);
}



/* XXX: what do we do about wrapping these? maybe operate on the boundaries as a list or tuple like libxml2 does? */
/**
 * Extract a LibXML node set pointer from a Python object.
 *
 * @param obj The Python object to extract the pointer from.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 * @see xmlsec1mod_unwrap_libxml_pointer
 */
xmlNodeSetPtr xmlsec1mod_unwrap_xmlNodeSetPtr(PyObject * obj)
{
    return (xmlNodeSetPtr)xmlsec1mod_unwrap_libxml_pointer(obj, xmlNodeSetPtrName);
}



/**
 * Extract a LibXML output buffer pointer from a Python object.
 *
 * @param obj The Python object to extract the pointer from.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 * @see xmlsec1mod_unwrap_libxml_pointer
 */
xmlOutputBufferPtr xmlsec1mod_unwrap_xmlOutputBufferPtr(PyObject * obj)
{
    return (xmlOutputBufferPtr)xmlsec1mod_unwrap_libxml_pointer(obj, xmlOutputBufferPtrName);
}



/* XXXXXXXXXXXXX: this is not right, is it? */
/**
 * Wrap a LibXML output buffer pointer in a Python object.
 *
 * @param ptr The object to wrap.
 * @return The encapsulated object.
 * @see xmlsec1mod_unwrap_xmlOutputBufferPtr
 */
PyObject * xmlsec1mod_wrap_xmlOutputBufferPtr(xmlOutputBufferPtr ptr)
{
    return xmlsec1_wrapLibxml2PtrInPyObject(ptr, xmlOutputBufferPtrName);
}




/**
 * Extract a LibXML parser input buffer pointer from a Python object.
 *
 * @param obj The Python object to extract the pointer from.
 * @return The decapsulated object pointer or NULL if an error occurred. If NULL is returned a Python exception will be set.
 * @see xmlsec1mod_unwrap_libxml_pointer
 */
xmlParserInputBufferPtr xmlsec1mod_unwrap_xmlParserInputBufferPtr(PyObject * obj)
{
    return (xmlParserInputBufferPtr)xmlsec1mod_unwrap_libxml_pointer(obj, xmlParserInputBufferPtrName);
}



/* XXX: not entirely certain what to do here - seems like it's just a PyCObject */
/**
 * Wrap a LibXML parser input buffer pointer in a Python object.
 *
 * @param ptr The object to wrap.
 * @return The encapsulated object.
 * @see xmlsec1mod_unwrap_xmlOutputBufferPtr
 */
PyObject * xmlsec1mod_wrap_xmlParserInputBufferPtr(xmlParserInputBufferPtr ptr)
{
    PyObject * obj;

    if (NULL == ptr)
    {
        PyErr_SetString(PyExc_ValueError, "pointer to wrap may not be NULL");
        return NULL;
    }

    if (NULL == (obj = PyCObject_FromVoidPtrAndDesc((void *)ptr, (void *)xmlParserInputBufferPtrName, NULL)))
    {
        ENSURE_EXCEPTION_SET(PyCObject_FromVoidPtrAndDesc);
        return NULL;
    }

    return obj;
}

#endif
